<?php

/**
 * ####### 	######## 		#####		##### 
 * #      #	#	    #		#	#		##### 
 * #      #	#	    #		#	#		
 * ####### 	#	####		#	#		#####
 * #      #	#	#			#	#######	#	#
 * #      #	#	#			#		  #	#	#
 * #######	#####			###########	#####
 *
 * Bullet Proof Lib Project
 * 
 * @author		David ansermot <david@ansermot.ch>
 * @date		2009-09-01
 * @version		0.1.0
 * @package		bplib
 * @copyright	(C)2009 David Ansermot
 * @license		GNU/GPL v3 - <http://www.gnu.org/licenses/>
 *
 **********************************************************************/

/**
 *
 * Bullet Proof Lib
 * 
 * @author		David ansermot <david@ansermot.ch>
 * @date		2009-09-01
 * @version		1.0.4
 * @package		bplib
 * @subpackage	bplib_v1_0_0
 * @copyright	(C)2009 David Ansermot
 * @file		lib.bplib.php
 *
 **********************************************************************
 
	This file is part of Bullet Proof Lib.

    Foobar is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Foobar is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.                                      
   
 **********************************************************************
 *                                                                   
 */

/**
 * Methods available
 * - init()
 * - loadPackage()
 * - path()
 */

/**
 * @use $loaded = BpLib::init('', array('bpsessionhandler', 'bpregistry'));
 * @use $pLoaded = BpLib::loadPackage('bprequest');
 * @use $newPath = BPLib::path('customPackages/');
 */

class BpLib {
	
	// Default package folder
	const BP_CLASSES_FOLDER = '/packages';
	
	// Complete path to packages
	protected static $path = '';
	
	// Interface declaration file
	protected static $interfaces = 'interfaces.php';
	
	// Classes instances
	protected static $instances = array();
	
	// Packages list to load
	protected static $packages = array(
		'Bp_SessionHandler',
		//'bpdatabasemysql', // UNSAFE. MAKE CRASH
		'Bp_Registry',
		'Bp_Tools',
		'Bp_Document',
	);
	
	
	/**
	 * Init the library
	 *
	 * @param	string	$folder		Package folder (optional)
	 * @param	array	$packages	Package list to load (optional)
	 * @return	mixed	Bool or BpError
	 * @access	public
	 * @static
	 */
	public static function init($folder = '', array $packages = array(), $empty = false) {
		
		self::$interfaces = dirname(__FILE__).'/'.rtrim(self::$interfaces, '/');
		
		// Determine the folder to use
		if (!empty($folder)) {
			self::$path = $folder.'/';
		} else {
			self::$path = dirname(__FILE__).rtrim(self::BP_CLASSES_FOLDER, '/').'/';
		}
		
		// Determine the packages to load
		if (is_array($packages)) {
			if ((count($packages) == 0 && $empty) || count($packages) > 0) {
				self::$packages = $packages;
			}
		}
		
		// File system check
		if (!self::checkFileSystem()) {
			return false;
		}
		
		include_once(self::$interfaces);
		
		// Security check for BpError package (mandatory package)
		if (self::loadPackage('BpError') !== true) {
			return false;
		}
		
		// Load each package
		foreach (self::$packages as $package) {
			
			// Security check
			$response = self::loadPackage($package);
			if ($response === false || BpError::is($response)) {
				// False or BpError
				return $response;
			}
		}
		
		return true;
	} 
	
	
	/**
	 * Load a package
	 *
	 * @param	string	$package	The package to load
	 * @return	mixed	Bool or BpError
	 * @access	public
	 * @static
	 */
	public static function loadPackage($package) {
		
		// Formating package's name
		$package = strtolower($package);
		// Getting segments
		$packSegments = explode('.', $package);
		// Getting last (real package name)
		$packLast = array_pop($packSegments);
		// Getting the base package
		$packBase = array_pop(rsort($packSegments));
		rsort($packSegments);
		// Building real package filename
		$packName = 'class.'.implode('_', $packSegments).'_'.$packLast.'.php';
		// Getting final path
		$tmpPath = (count($packSegments) > 0) ? implode('/', $packSegments) : '';
		$packPath = self::$path.$packBase.$tmpPath.$packName;
		
		// Security check
		if (is_file($packPath) && file_exists($packPath)) {
			require_once($packPath);
		} else {
			// Right error message type
			if (class_exists('BpError')) {
				return BpError::get('Unable to load package <'.$package.'>', 'Error', BpError::BP_ERROR_FATAL);
			} else {
				return false;
			}
		}
		
		// Try to load dependencies if has
	/*	if ($package::hasDeps()) {
			
			$deps = $package::getDeps();
			foreach ($deps as $dependence) {
				
				$depPath = self::$path.$package.'/class.'.strtolower($dependence).'.php';
				// If dependence isn't loaded
				if (!class_exists($dependence)) {
					
					// If package's file doesn't exists
					if (!file_exists($depPath) || !is_file($depPath)) {
						return BpError::get('Unable to load package <'.$package.'> dependence <strong>'.$dependence.'</strong>', 'Error', BpError::BP_ERROR_FATAL);
					}
					require_once($depPath);
				}
			}
		}*/
		
		return true;
	}
	
	/**
	 * Return or set the packages path
	 *
	 * @param	string	$path	The new path (optional)
	 * @return	string	The path
	 * @access	public
	 * @static
	 */
	public static function path($path = '') {
		
		if (!empty($path)) {
			self::$path = trim($path);
		} 
		return self::$path;
		
	}
	
	
	/**
	 * Return an instance of a package
	 *
	 * @param	string	$class: Class's name
	 * @param	bool	$override: If true, erase existing instance (if not singleton)
	 * @return	mixed	BpError(::BP_ERROR_FATAL) or the object
	 * @access	public
	 * @static
	 */
	public static function &getInstance($class, $override = false) {
		
		// @todo	: check performances !!
		$class = implode('_', array_map(ucfirst, explode('.', $class)));
		if (!class_exists($class)) {
			return BpError::get('Object can\'t be created, missing class <'.$class.'> declaration.', 'BpError [FATAL]', BpError::BP_ERROR_FATAL);
		}
		
		// Make a new instance if :  
		//   1) instance not set  OR 
		//   2) instance isn't a singleton AND override is needed
		if (!isset(self::$instances[$class]) || (!self::isSingleton($class) && $override)) {
			
			// If extra arguments
			if (func_num_args() > 1) {
			
				// Getting args
				$args = func_get_args();
				array_shift($args);
				
				// Creating instance
				$reflectionClass = new ReflectionClass($class);
				$object = $reflectionClass->newInstanceArgs($args);
				self::$instances[$class] = &$object;
				
			} else {
				self::$instances[$class] = new $class;
			}
		}
		
		return self::$instances[$class];
	}
	
	
	/**
	 * Check if object is a singleton
	 *
	 * @param	obj		$class: The class to check
	 * @return	bool	
	 * @access	public
	 * @static
	 */
	public static function isSingleton($class) {
		return ($class instanceof IBpSingleton);
	}
	
	
	/**
	 * Check file system authenticity
	 *
	 * @param	void
	 * @return	bool	False on error, else true
	 * @access	protected
	 * @static
	 */
	protected static function checkFileSystem() {
		
		// Folders checks
		if (!is_dir(self::$path)) {
			return false;
		}
		
		// Intefaces check
		if (!file_exists(self::$interfaces) || !is_file(self::$interfaces)) {
			return false;
		} 
		
		return true;
	}
	
}

?>